package compile

// Abstract Declarations

type Node interface {
	Pos() Pos
	vfyNode()
}

type node struct{ pos Pos }

func (n *node) vfyNode() {}
func (n *node) Pos() Pos { return n.pos }

type Declare interface {
	Node
	vfyDecl()
}

type declare struct{ node }

func (d *declare) vfyDecl() {}

type Expression interface {
	Node
}

type expression struct {
	node
}

type MemberDeclare interface {
	Declare
	vfyMDeclare()
}

type memberDeclare struct{ declare }

func (m *memberDeclare) vfyMDeclare() {}

type Statement interface {
	Node
	vfyStmt()
}

type statement struct{ node }

func (s *statement) vfyStmt() {}

// Real Nodes

type ClassFile struct {
	node

	ClassFileName   string
	ClassFilePath   string
	PublicClassName string

	Package *PackageDeclare // nil if 'package .'
	Imports []*ImportDeclare

	Decls []MemberDeclare
}

type Identifier struct {
	node

	Value string
}

type DotPath struct {
	node

	Parts        []*Identifier
	WithWildcard bool
}

type MemberProperty struct {
	node

	AccessModifiers uint8
	IsStatic        bool
	IsConst         bool
}

type ArgList struct {
	node

	PlainArg []Expression
}

// MemberDeclare

type EnumItem struct {
	node

	Name  *Identifier
	Value *CellExpr
}

type EnumDeclare struct {
	memberDeclare

	Property *MemberProperty
	Name     *Identifier
	ItemList []*EnumItem
}

type ClassDeclare struct {
	memberDeclare

	IsInterface    bool
	Property       *MemberProperty
	GenericTypes   *ClassGeneric
	ExtendsClass   *ClassAccessExpr
	InterfaceNames []*ClassAccessExpr
	Members        []MemberDeclare
	OpOverloads    []*OperatorOverloadDecl
	Name           *Identifier
}

type AnonymousClassDecl struct {
	memberDeclare

	BaseClass   *ClassAccessExpr
	Members     []MemberDeclare
	OpOverloads []*OperatorOverloadDecl
}

type OverrideClassDecl struct {
	memberDeclare

	OverrideClass *ClassAccessExpr
	Block         *BlockStmt
}

type InterfaceDeclare struct {
	memberDeclare

	Property           *MemberProperty
	ExtendsInterfaces  []*ClassAccessExpr
	InterfaceNames     []*ClassAccessExpr
	MethodDeclarations []MemberDeclare
	GenericTypes       *ClassGeneric
	Name               *Identifier
}

type MethodDefine struct {
	memberDeclare

	Property     *MemberProperty
	ReturnType   *TypeExpr // nil if void
	Parameters   *ParameterList
	Body         *BlockStmt // nil if native
	Name         *Identifier
	GenericNames *ClassGeneric
	IsNative     bool
}

type MethodDeclare struct {
	memberDeclare

	Property     *MemberProperty
	Parameters   *ParameterList
	ReturnType   *TypeExpr // nil if void
	Name         *Identifier
	GenericNames *ClassGeneric
}

type ConstructorStmt struct {
	memberDeclare

	Property *MemberProperty
	Name     *Identifier
	Params   *ParameterList
	Body     *BlockStmt
}

type OperatorOverloadDecl struct {
	memberDeclare

	Op           operator
	Params       *ParameterList
	Body         *BlockStmt
	ReturnType   *TypeExpr
	GenericNames *ClassGeneric
}

type ParameterList struct {
	node

	Params   []*PlainParameter
	VarParam *VarParam // Nil if no Var Param
}

type VarParam struct {
	node

	Name *Identifier
	Type *TypeExpr
}

type PlainParameter struct {
	node

	Type *TypeExpr
	Name *Identifier
}

type FieldDeclare struct {
	memberDeclare

	Property *MemberProperty
	Names    []*Identifier
	Type     *TypeExpr
	Value    Expression
}

// Declare

type ImportDeclare struct {
	declare

	/*
		 A) import static movies.nezha.*  // Invalid !
				Cannot use 'static' when importing a package
		 B) import movies.nezha.Nezha as Zhaer // Valid !
		 C) import static movies.nezha.Nezha // Valid !
		 D) import movies.nezha.* as NEZHA // Invalid !
				Cannot use '*' when importing a package
	*/

	ImportParts  *DotPath
	IsStatic     bool // nil if no 'static'
	WithWildcard bool
	Name         string // nil if no 'as'

	// to save a string slice style of ImportParts,
	// enable in semantic analyze (see import.go)
	// StringParts []string
}

type PackageDeclare struct {
	declare

	PackagePath *DotPath
}

type ArrayBody struct {
	expression

	ArrDepth int /* for n-dimensional array */
	Items    []Expression
}

// Expression

/*
type RightExpr struct {
	expression

	OpType token
	Op     operator
	Expr   Expression
}
*/

type TypeConvertExpr struct {
	expression

	Type *TypeExpr
	Expr Expression
}

type BinaryExpr struct {
	expression

	LHS    Expression
	RHS    Expression
	Op     operator
	Assign bool // for assign op
}

type TernaryExpr struct {
	expression

	Cond Expression
	A    Expression
	B    Expression
}

type IncreaseExpr struct {
	expression

	Behind bool
	Op     token
	Right  Expression
}

type NotExpr struct {
	expression

	Right Expression
}

type NormalUnaryExpr struct {
	expression

	Op    token
	Right Expression
}

type ClassAccessCell struct {
	node

	GenericTypes []*TypeExpr // empty if no generic
	ClassName    string
}

type ClassAccessExpr struct {
	expression

	Classes []*ClassAccessCell
}

type TypeExpr struct {
	expression

	// []String
	ArrayDecls []*ArrayDeclare
	Factor     *TypeFactor
}

type ArrayDeclare struct {
	node

	/* Valid if in var declaration. Usually, it is nil */
	Size Expression // It can be any expression that can return an integer
}

type TypeFactor struct {
	node

	UserType *ClassAccessExpr // nil if base type
	BaseType *Identifier      // nil if user type
}

// Generic declare for class declare
type ClassGeneric struct {
	node

	ClassNames []*Identifier
}

type CallExpr struct {
	expression

	Left         Expression
	Args         *ArgList
	GenericTypes []*TypeExpr
}

type SubscrExpr struct {
	expression

	Left  Expression
	Right Expression
}

type AccessExpr struct {
	expression

	Left  Expression
	Right Expression
}

type NewExpr struct {
	expression

	/*
		A) When the constructed class has no constructor
			(for example, no constructor is explicitly declared),
			Params is nil.
		B) When the class array is not declared,
			ArrFactor is nil.
	*/

	Type           *TypeExpr
	ArgList        *ArgList            // if it needs
	ArrFactor      *ArrayBody          // if it needs
	AnonymousClass *AnonymousClassDecl // if it has
	OverrideClass  *OverrideClassDecl  // if it has
}

type CellExpr struct {
	expression

	CellType token

	String  string
	Ident   string
	Integer uint64
	Float   float64

	Raw string
}

// Statements

type BlockStmt struct {
	node

	Stmts []Statement
}

type ExprStmt struct {
	statement

	Expr Expression
}

type LabelStmt struct {
	statement

	Label string
}

type GotoStmt struct {
	statement

	Label string
}

type WhileStmt struct {
	statement

	Test     Expression
	Body     *BlockStmt
	ThenBody *BlockStmt
}

type ForStmt struct {
	statement

	Init   []*VarStmt   // Empty if no initialization
	Test   Expression   // Nil if no test (default True)
	Update []Expression // Empty if no update

	Body     *BlockStmt
	ThenBody *BlockStmt
}

type ForEachStmt struct {
	statement

	Var    *VarStmt
	Object Expression

	Body     *BlockStmt
	ThenBody *BlockStmt
}

type SwitchStmt struct {
	statement

	Target Expression
	Clause []*SwitchCase
}

type SwitchCase struct {
	node

	Case []Expression // nil in default case
	Body []Statement
}

type ReturnStmt struct {
	statement

	Expr Expression
}

type ContinueStmt struct {
	statement

	Label string // "" if no label
}

type BreakStmt struct {
	statement

	Label string // "" if no label
}

type ThrowStmt struct {
	statement

	Expr Expression
}

type AssertStmt struct {
	statement

	Expr Expression
}

type IfStmt struct {
	statement

	Test     Expression
	IfBody   *BlockStmt
	Elifs    []*IfStmt  // len == 0 if no elif
	ElseBody *BlockStmt // nil if no else
}

type TryStmt struct {
	statement

	TryBlock *BlockStmt
	Cases    []*CatchCase
	Finally  *BlockStmt
}

type CatchCase struct {
	node

	Case *ClassAccessExpr
	Name *Identifier
	Body *BlockStmt
}

type VarStmt struct {
	statement

	Names []*Identifier
	Type  *TypeExpr  // Nil if with type inference
	Value Expression // Nil if no value (declare only).
}
